package com.glSto.service;

import com.glSto.domain.*;
import com.glSto.rep.ResponseData;
import com.glSto.req.GoodsIssueRequest;
import com.glSto.req.PurchaseRequest;
import org.modelmapper.ModelMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Service
public class MdmService {
    private static final Logger mainDataLogger = LoggerFactory.getLogger("com.glSto.controller.MdmController.mainData");
    private static final Logger purchaseDataLogger = LoggerFactory.getLogger("com.glSto.controller.MdmController.purchaseData");
    private static final Logger goodsIssueLogger = LoggerFactory.getLogger("com.glSto.controller.MdmController.goodsIssue");

    // 接口地址
    @Value("${mdm.material.source-url}")
    private String apiUrl;

    @Value("${mdm.purchase.source-url:}")
    private String purchaseApiUrl;

    @Value("${mdm.issue.source-url:}")
    private String goodsIssueApiUrl;

    @Value("${mdm.basicAuth.username}")
    private String userName;

    @Value("${mdm.basicAuth.password}")
    private String passWord;

    // 表名常量（固定两个表名，循环复用）
    private static final String REPORT_TABLE = "ERP_MATERIAL_REPORT";  // 报表查询的正式表名
    private static final String TEMP_TABLE = "ERP_MATERIAL_TEMP";      // 同步用的临时表名

    // 同步参数配置
    private static final int BATCH_SIZE = 10000;         // 批量插入批次大小
    private static final int CONNECT_TIMEOUT = 15000;   // 接口连接超时（ms）
    private static final int READ_TIMEOUT = 60000;      // 接口读取超时（ms）
    private static final int THREAD_COUNT = 5;          // 并发线程数
    private static final int pageSize = 10000;

    // 组件
    private RestTemplate restTemplate;
    private ExecutorService executorService;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    // 初始化资源
    @PostConstruct
    public void init() {
        // 初始化RestTemplate（带超时配置）
        restTemplate = new RestTemplate();
        SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setConnectTimeout(CONNECT_TIMEOUT);
        requestFactory.setReadTimeout(READ_TIMEOUT);
        restTemplate.setRequestFactory(requestFactory);
        mainDataLogger.info("RestTemplate初始化完成 - 连接超时: {}ms, 读取超时: {}ms", CONNECT_TIMEOUT, READ_TIMEOUT);

        // 初始化线程池
        executorService = Executors.newFixedThreadPool(THREAD_COUNT);
        mainDataLogger.info("线程池初始化完成 - 线程数: {}", THREAD_COUNT);
    }

    /**
     * 核心方法：同步最新数据到报表（双表循环复用，无查询间隙）
     */
    @Transactional(rollbackFor = Exception.class)  // 事务保证：失败时回滚所有操作
    public String getMainData(String username, String password) {
        long startTime = System.currentTimeMillis();
        mainDataLogger.info("=== 开始报表数据同步（双表循环复用模式） ===");

        try {
            // 1. 从接口获取全量最新数据
            List<ResponseData.Collection.Items> allItems = fetchAllData(username, password);
            if (allItems.isEmpty()) {
                mainDataLogger.warn("接口返回空数据，同步终止（保留现有报表数据）");
                return "同步完成：接口返回空数据，报表数据未更新";
            }
            mainDataLogger.info("接口数据获取完成，共 {} 条", allItems.size());

            // 2. 清空临时表（无论临时表是新表还是上次的正式表，确保为空）
            clearTable(TEMP_TABLE);
            mainDataLogger.info("临时表 {} 已清空", TEMP_TABLE);

            // 3. 批量插入新数据到临时表（此过程不影响正式表查询）
            int insertCount = batchInsertToTable(allItems);
            mainDataLogger.info("新数据已写入临时表 {}，共 {} 条", TEMP_TABLE, insertCount);

            // 4. 原子性切换临时表和正式表（仅重命名，毫秒级完成）
            switchTables();
            mainDataLogger.info("表切换完成：临时表已变为正式表，原正式表变为新临时表");

            // 5. 同步完成
            mainDataLogger.info("=== 报表数据同步成功，总耗时: {}ms ===", System.currentTimeMillis() - startTime);
            return String.format("报表数据同步成功，最新数据量: %d 条", insertCount);

        } catch (Exception e) {
            mainDataLogger.error("同步失败（事务已回滚，不影响现有报表数据）", e);
            return "同步失败：" + e.getMessage();
        }
    }

    /**
     * 从接口分页获取全量数据
     */
    private List<ResponseData.Collection.Items> fetchAllData(String username, String password) {
        List<ResponseData.Collection.Items> allItems = new ArrayList<>();
        int page = 1;
        boolean hasMoreData = true;

        // 构建Basic认证请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        String auth = username + ":" + password;
        byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes());
        headers.set("Authorization", "Basic " + new String(encodedAuth));

        while (hasMoreData) {
            String paginatedUrl = apiUrl + "?$page=" + page + "&$limit=" + pageSize;
            mainDataLogger.info("获取接口第 {} 页数据", page);

            try {
                HttpEntity<String> entity = new HttpEntity<>("", headers);
                ResponseData responseData = restTemplate.postForObject(paginatedUrl, entity, ResponseData.class);

                // 校验数据
                if (responseData == null || responseData.getCollection() == null) {
                    mainDataLogger.info("第 {} 页无有效数据，终止获取", page);
                    hasMoreData = false;
                    continue;
                }

                List<ResponseData.Collection.Items> items = responseData.getCollection().getItems();
                if (items.isEmpty()) {
                    mainDataLogger.info("第 {} 页数据为空，终止获取", page);
                    hasMoreData = false;
                } else {
                    allItems.addAll(items);
                    mainDataLogger.info("第 {} 页获取 {} 条数据，累计 {} 条", page, items.size(), allItems.size());
                    // 若当前页数据不足1000，视为最后一页
                    if (items.size() < pageSize) {
                        hasMoreData = false;
                    }
                }
            } catch (Exception e) {
                mainDataLogger.error("获取第 {} 页数据失败", page, e);
                throw new RuntimeException("接口数据获取失败，同步终止", e);
            }

            page++;
        }

        return allItems;
    }

    /**
     * 清空指定表（同步前确保临时表为空）
     */
    private void clearTable(String tableName) {
        String sql = "DELETE FROM " + tableName;
        int deleteCount = jdbcTemplate.update(sql);
        mainDataLogger.info("清空表 {} 完成，共删除 {} 条旧数据", tableName, deleteCount);
    }

    /**
     * 批量插入数据到指定表（临时表或正式表）
     */
    private int batchInsertToTable(List<ResponseData.Collection.Items> items) {
        int total = items.size();
        int batchCount = (total + BATCH_SIZE - 1) / BATCH_SIZE;
        mainDataLogger.info("开始批量插入到 {} - 总批次数: {}", MdmService.TEMP_TABLE, batchCount);

        AtomicInteger totalSuccess = new AtomicInteger(0);
        List<CompletableFuture<Void>> futures = new ArrayList<>();

        // 按批次提交插入任务
        for (int i = 0; i < total; i += BATCH_SIZE) {
            int start = i;
            int end = Math.min(i + BATCH_SIZE, total);
            List<ResponseData.Collection.Items> batch = items.subList(start, end);
            String batchName = String.format("批次[%d-%d]", start + 1, end);

            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                long batchStartTime = System.currentTimeMillis();
                try {
                    // 解析当前批次数据
                    List<GoodsData> dataList = new ArrayList<>();
                    for (ResponseData.Collection.Items item : batch) {
                        Map<String, String> fieldMap = parseItemData(item);
                        GoodsData data = convertToGoodsData(fieldMap);
                        dataList.add(data);
                    }

                    // 执行批量插入
                    String sql = String.format("INSERT INTO %s (" +
                                    "MATNR, BWKEY, BWTAR, MAKTX, MATKL, WERKS, EKGRP, MEINS, VERPR, R" +
                                    ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
                            MdmService.TEMP_TABLE);

                    jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
                        @Override
                        public void setValues(PreparedStatement ps, int idx) throws SQLException {
                            GoodsData data = dataList.get(idx);
                            ps.setString(1, data.matnr);
                            ps.setString(2, data.bwkey);
                            ps.setString(3, data.bwtar);
                            ps.setString(4, data.maktx);
                            ps.setString(5, data.matkl);
                            ps.setString(6, data.werks);
                            ps.setString(7, data.ekgrp);
                            ps.setString(8, data.meins);
                            ps.setString(9, data.verpr);
                            setIntOrNull(ps, 10, data.r);
                        }

                        @Override
                        public int getBatchSize() {
                            return dataList.size();
                        }
                    });

                    totalSuccess.addAndGet(dataList.size());
                    mainDataLogger.info("{} 插入完成 - 成功: {} 条, 耗时: {}ms",
                            batchName, dataList.size(), System.currentTimeMillis() - batchStartTime);

                } catch (Exception e) {
                    mainDataLogger.error("{} 插入失败", batchName, e);
                    throw new RuntimeException("批次插入失败：" + batchName, e);
                }
            }, executorService);

            futures.add(future);
        }

        // 等待所有批次完成
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

        if (totalSuccess.get() != total) {
            throw new RuntimeException("批量插入不完整 - 总数据: " + total + ", 成功: " + totalSuccess.get());
        }

        return totalSuccess.get();
    }

    /**
     * 核心：原子性切换正式表和临时表（仅重命名，循环复用）
     */
    private void switchTables() {
        // 步骤1：先将当前正式表重命名为临时表（下次同步用）
        String renameReportToTemp = "ALTER TABLE " + REPORT_TABLE + " RENAME TO " + TEMP_TABLE + "_SWAP";
        // 步骤2：将当前临时表（含新数据）重命名为正式表（报表查询用）
        String renameTempToReport = "ALTER TABLE " + TEMP_TABLE + " RENAME TO " + REPORT_TABLE;
        // 步骤3：将步骤1的表重命名为临时表（完成循环）
        String renameSwapToTemp = "ALTER TABLE " + TEMP_TABLE + "_SWAP" + " RENAME TO " + TEMP_TABLE;

        // 执行切换（通过中间名避免表名冲突）
        jdbcTemplate.execute(renameReportToTemp);
        jdbcTemplate.execute(renameTempToReport);
        jdbcTemplate.execute(renameSwapToTemp);
    }

    // 辅助方法：解析Item数据为字段映射
    private Map<String, String> parseItemData(ResponseData.Collection.Items item) {
        try {
            List<ResponseData.Collection.Items.Data> dataList = item.getData();
            if (dataList == null || dataList.isEmpty()) {
                mainDataLogger.warn("Item无数据字段，插入空值");
                return new HashMap<>();
            }
            return dataList.stream()
                    .collect(Collectors.toMap(
                            ResponseData.Collection.Items.Data::getName,
                            ResponseData.Collection.Items.Data::getValue,
                            (existing, replacement) -> replacement // 重复字段取后者
                    ));
        } catch (Exception e) {
            mainDataLogger.error("解析Item数据失败，插入空值", e);
            return new HashMap<>();
        }
    }

    // 辅助方法：转换为插入数据对象
    private GoodsData convertToGoodsData(Map<String, String> fieldMap) {
        GoodsData data = new GoodsData();
        data.matnr = fieldMap.get("MATNR");
        data.bwkey = fieldMap.get("BWKEY");
        data.bwtar = fieldMap.get("BWTAR");
        data.maktx = fieldMap.get("MAKTX");
        data.matkl = fieldMap.get("MATKL");
        data.werks = fieldMap.get("WERKS");
        data.ekgrp = fieldMap.get("EKGRP");
        data.meins = fieldMap.get("MEINS");
        data.verpr = fieldMap.get("VERPR");
        data.r = parseNumber(fieldMap.get("R"));
        return data;
    }

    // 辅助方法：字符串转数字（允许空值）
    private Integer parseNumber(String value) {
        if (value == null || value.trim().isEmpty()) {
            return null;
        }
        try {
            return Integer.parseInt(value.trim());
        } catch (NumberFormatException e) {
            mainDataLogger.warn("字段R无法转换为数字，存入null: {}", value);
            return null;
        }
    }

    // 辅助方法：PreparedStatement设置int或null
    private void setIntOrNull(PreparedStatement ps, int index, Integer value) throws SQLException {
        if (value == null) {
            ps.setNull(index, java.sql.Types.INTEGER);
        } else {
            ps.setInt(index, value);
        }
    }

    // 销毁资源
    @PreDestroy
    public void destroy() {
        if (executorService != null && !executorService.isShutdown()) {
            executorService.shutdown();
            try {
                if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
                    executorService.shutdownNow();
                }
            } catch (InterruptedException e) {
                executorService.shutdownNow();
                Thread.currentThread().interrupt();
            }
            mainDataLogger.info("线程池已关闭");
        }
    }

    /**
     * 内部类：封装插入数据
     */
    private static class GoodsData {
        String matnr;
        String bwkey;
        String bwtar;
        String maktx;
        String matkl;
        String werks;
        String ekgrp;
        String meins;
        String verpr;
        Integer r;
    }

    public PurchaseResponse getPurchaseData(PurchaseRequest request) {
        purchaseDataLogger.info("=== 开始获取采购数据 ===");

        try {
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            String auth = userName + ":" + passWord;
            byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes());
            headers.set("Authorization", "Basic " + new String(encodedAuth));


            // 初始化映射器
            ModelMapper modelMapper = new ModelMapper();

            PurchaseRequestDTO.ImMessage dtoImMessage = modelMapper.map(
                    request.getIM_MESSAGE(),  // 源对象：PurchaseRequest.ImMessage
                    PurchaseRequestDTO.ImMessage.class  // 目标类型
            );

            List<PurchaseRequestDTO.ItData> dtoItDataList = request.getIT_DATA().stream()
                    .map(itData -> modelMapper.map(itData, PurchaseRequestDTO.ItData.class))
                    .collect(Collectors.toList());

            PurchaseRequestDTO dto = new PurchaseRequestDTO();
            dto.setIM_MESSAGE(dtoImMessage);
            dto.setIT_DATA(dtoItDataList);

            HttpEntity<PurchaseRequestDTO> entity = new HttpEntity<>(dto, headers);

            return restTemplate.postForObject(purchaseApiUrl, entity, PurchaseResponse.class);


        } catch (Exception e) {
            purchaseDataLogger.error("获取采购数据失败", e);
            throw new RuntimeException("获取采购数据失败: " + e.getMessage(), e);
        }
    }

    public GoodsIssueResponse getGoodsIssueData(GoodsIssueRequest request) {
        goodsIssueLogger.info("=== 开始获取物资出库数据 ===");

        try {
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            String auth = userName + ":" + passWord;
            byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes());
            headers.set("Authorization", "Basic " + new String(encodedAuth));

            // 初始化映射器
            ModelMapper modelMapper = new ModelMapper();

            GoodsIssueRequestDTO.ImMessage dtoImMessage = modelMapper.map(
                    request.getIM_MESSAGE(),  // 源对象：GoodsIssueRequestDTO.ImMessage
                    GoodsIssueRequestDTO.ImMessage.class  // 目标类型
            );

            List<GoodsIssueRequestDTO.ItData> dtoItDataList = request.getIT_DATA().stream()
                    .map(itData -> modelMapper.map(itData, GoodsIssueRequestDTO.ItData.class))
                    .collect(Collectors.toList());

            GoodsIssueRequestDTO dto = new GoodsIssueRequestDTO();
            dto.setIM_MESSAGE(dtoImMessage);
            dto.setIT_DATA(dtoItDataList);

            HttpEntity<GoodsIssueRequestDTO> entity = new HttpEntity<>(dto, headers);

            return restTemplate.postForObject(goodsIssueApiUrl, entity, GoodsIssueResponse.class);


        } catch (Exception e) {
            goodsIssueLogger.error("获取物资出库数据失败", e);
            throw new RuntimeException("获取物资出库数据失败: " + e.getMessage(), e);
        }
    }
}